home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / Dialing Addresses / Src / AddressLookup.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  57.3 KB  |  1,836 lines

  1. /***********************************************************************
  2.  *
  3.  Copyright © 1995 - 1998, 3Com Corporation or its subsidiaries ("3Com").  
  4.  All rights reserved.
  5.    
  6.  This software may be copied and used solely for developing products for 
  7.  the Palm Computing platform and for archival and backup purposes.  Except 
  8.  for the foregoing, no part of this software may be reproduced or transmitted 
  9.  in any form or by any means or used to make any derivative work (such as 
  10.  translation, transformation or adaptation) without express written consent 
  11.  from 3Com.
  12.  
  13.  3Com reserves the right to revise this software and to make changes in content 
  14.  from time to time without obligation on the part of 3Com to provide notification 
  15.  of such revision or changes.  
  16.  3COM MAKES NO REPRESENTATIONS OR WARRANTIES THAT THE SOFTWARE IS FREE OF ERRORS 
  17.  OR THAT THE SOFTWARE IS SUITABLE FOR YOUR USE.  THE SOFTWARE IS PROVIDED ON AN 
  18.  "AS IS" BASIS.  3COM MAKES NO WARRANTIES, TERMS OR CONDITIONS, EXPRESS OR IMPLIED, 
  19.  EITHER IN FACT OR BY OPERATION OF LAW, STATUTORY OR OTHERWISE, INCLUDING WARRANTIES, 
  20.  TERMS, OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND 
  21.  SATISFACTORY QUALITY.
  22.  
  23.  TO THE FULL EXTENT ALLOWED BY LAW, 3COM ALSO EXCLUDES FOR ITSELF AND ITS SUPPLIERS 
  24.  ANY LIABILITY, WHETHER BASED IN CONTRACT OR TORT (INCLUDING NEGLIGENCE), FOR 
  25.  DIRECT, INCIDENTAL, CONSEQUENTIAL, INDIRECT, SPECIAL, OR PUNITIVE DAMAGES OF 
  26.  ANY KIND, OR FOR LOSS OF REVENUE OR PROFITS, LOSS OF BUSINESS, LOSS OF INFORMATION 
  27.  OR DATA, OR OTHER FINANCIAL LOSS ARISING OUT OF OR IN CONNECTION WITH THIS SOFTWARE, 
  28.  EVEN IF 3COM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
  29.  
  30.  3Com, HotSync, Palm Computing, and Graffiti are registered trademarks, and 
  31.  Palm III and Palm OS are trademarks of 3Com Corporation or its subsidiaries.
  32.  
  33.  IF THIS SOFTWARE IS PROVIDED ON A COMPACT DISK, THE OTHER SOFTWARE AND 
  34.  DOCUMENTATION ON THE COMPACT DISK ARE SUBJECT TO THE LICENSE AGREEMENT 
  35.  ACCOMPANYING THE COMPACT DISK.
  36.  
  37.  **********************************************************************/
  38.  
  39. #include <Pilot.h>
  40.  
  41. #include "AddressDB.h"
  42. #include "Address.h"
  43. #include "AddressRsc.h"
  44.  
  45. #pragma mark ----------------
  46. /***********************************************************************
  47.  *
  48.  * FUNCTION:    LookupDrawRecordFields
  49.  *
  50.  * DESCRIPTION: Draws the name and phone number (plus which phone)
  51.  * within the screen bounds passed.
  52.  *
  53.  * PARAMETERS:  vars - variables used by the lookup code.
  54.  *              record - record to draw
  55.  *              field - phone to draw
  56.  *              bounds - bounds of the draw region
  57.  *              phoneLabelLetters - the first letter of each phone label
  58.  *              sortByCompany - true if the database is sorted by company
  59.  *
  60.  * RETURNED:    nothing
  61.  *
  62.  * REVISION HISTORY:
  63.  *         Name   Date      Description
  64.  *         ----   ----      -----------
  65.  *         roger   6/21/95   Initial Revision
  66.  *
  67.  ***********************************************************************/
  68.  
  69. static void LookupDrawRecordFields (LookupVariablesPtr vars,
  70.    AddrDBRecordPtr record, UInt field, RectanglePtr bounds, 
  71.    CharPtr phoneLabelLetters)
  72. {
  73.    CharPtr name1;
  74.    CharPtr name2;
  75.    CharPtr field1;
  76.    CharPtr field2;
  77.    SWord x;
  78.    SWord y;
  79.    Int   field1Length;
  80.    Int   field2Length;
  81.    Int   field1Width;
  82.    Int   field2Width;
  83.    Int   horizontalSpace;
  84.    UInt  phoneLabel;
  85.    UInt  fieldSeparatorWidth;
  86.    UInt  shortenedFieldWidth;
  87.    UInt  field1Num;
  88.    UInt  field2Num;
  89.    Int  name1Length;
  90.    Int  name2Length;
  91.    Int  name1Width;
  92.    Int  name2Width;
  93.    Boolean ignored;
  94.    Boolean everythingFits;
  95.     Boolean name1HasPriority = true;
  96.     Byte phoneLabelWidth;
  97.     
  98.  
  99.    x = bounds->topLeft.x;
  100.    y = bounds->topLeft.y;
  101.    
  102.    phoneLabelWidth = FntCharWidth('W') - 1;        // remove the blank trailing column
  103.  
  104.  
  105. //   bounds->extent.x -= (phoneLabelWidth + 1);
  106.    
  107.    
  108.    // Do we need to figure out the person's name?
  109.    if (vars->params->field1 == addrLookupSortField ||
  110.       vars->params->field2 == addrLookupSortField)
  111.       {
  112.       name1HasPriority = DetermineRecordName (record, &shortenedFieldWidth,
  113.           &fieldSeparatorWidth, vars->sortByCompany, &name1, &name1Length, &name1Width, 
  114.          &name2, &name2Length, &name2Width, NULL, bounds->extent.x);
  115.       }
  116.    
  117.    // Handle the first field to display
  118.    field1Num = vars->params->field1;
  119.    if (field1Num == addrLookupSortField)
  120.       {
  121.       field1Width = name1Width + (name2 ? fieldSeparatorWidth : 0) +
  122.          name2Width;
  123.       }
  124.    else if (field1Num == addrLookupNoField)
  125.       {
  126.       field1Length = 0;
  127.       field1Width = 0;
  128.       }
  129.    else
  130.       {
  131.       // Map from the lookup field requested to the Address's format
  132.       if (IsPhoneLookupField(field1Num))
  133.          {
  134.          field1 = record->fields[field];
  135.          }
  136.       else if (field1Num == addrLookupListPhone)
  137.          {
  138.          field1 = record->fields[phone1 + record->options.phones.displayPhoneForList];
  139.          }
  140.       else
  141.          {
  142.          field1 = record->fields[vars->lookupFieldMap[field1Num]];
  143.          }
  144.       
  145.       if (field1)
  146.          {
  147.          // Only show text from the first line in the field
  148.          field1Width = bounds->extent.x;
  149.          field1Length = field1Width;      // more characters than we can expect
  150.          FntCharsInWidth (field1, &field1Width, &field1Length, &ignored);//lint !e64
  151.          
  152.          // Leave room for a letter to indicate which phone is being displayed.
  153.          if (field1Num == addrLookupListPhone)
  154.             field1Width += phoneLabelWidth + 1;
  155.          }
  156.       else
  157.          {
  158.          field1Length = 0;
  159.          field1Width = 0;
  160.          }
  161.       }
  162.       
  163.       
  164.    // Handle the second field to display
  165.    field2Num = vars->params->field2;
  166.    if (field2Num == addrLookupSortField)
  167.       {
  168.       // This is the width of the name before any truncation.
  169.       field2Width = name1Width + (name2 ? fieldSeparatorWidth : 0) +
  170.          name2Width;
  171.       }
  172.    else if (field2Num == addrLookupNoField)
  173.       {
  174.       field2Length = 0;
  175.       field2Width = 0;
  176.       }
  177.    else
  178.       {
  179.       // Map from the lookup field requested to the Address's format
  180.       if (IsPhoneLookupField(field2Num))
  181.          {
  182.          field2 = record->fields[field];
  183.          }
  184.       else if (field2Num == addrLookupListPhone)
  185.          {
  186.          field2 = record->fields[phone1 + record->options.phones.displayPhoneForList];
  187.          }
  188.       else
  189.          {
  190.          field2 = record->fields[vars->lookupFieldMap[field2Num]];
  191.          }
  192.       
  193.       if (field2)
  194.          {
  195.          // Only show text from the first line in the field
  196.          field2Width = bounds->extent.x;
  197.          field2Length = field2Width;      // more characters than we can expect
  198.          FntCharsInWidth (field2, &field2Width, &field2Length, &ignored);//lint !e64
  199.          
  200.          // Leave room for a letter to indicate which phone is being displayed.
  201.          if (vars->params->field2 == addrLookupListPhone)
  202.             field2Width += phoneLabelWidth + 1;
  203.          }
  204.       else
  205.          {
  206.          field2Length = 0;
  207.          field2Width = 0;
  208.          }
  209.       }
  210.  
  211.  
  212.  
  213.    // Now check if everything can display without any truncation.   
  214.    everythingFits = bounds->extent.x >= field1Width + 
  215.       ((field1Width && field2Width) ? spaceBetweenNamesAndPhoneNumbers : 0) + 
  216.       field2Width;
  217. /*   if (bounds->extent.x   >= name1Width + (name2 ? fieldSeparatorWidth : 0) +
  218.       name2Width + (phone ? spaceBetweenNamesAndPhoneNumbers : 0) + phoneWidth)
  219. */      {
  220.       }
  221.    
  222.    // Find out how much room the left side can use
  223.    if (everythingFits || field2Num == addrLookupNoField)
  224.       horizontalSpace = bounds->extent.x;
  225.    else
  226.       {
  227.       horizontalSpace = bounds->extent.x - spaceBetweenNamesAndPhoneNumbers;
  228.       
  229.       // This allows the left side to use space on the right side if the
  230.       // right side is small enough.
  231.       horizontalSpace -= min(horizontalSpace / 2, field2Width);
  232.       }
  233.    x = bounds->topLeft.x;
  234.    
  235.    
  236.    // Now draw the left side.
  237.    if (vars->params->field1 == addrLookupSortField)
  238.       {
  239.       DrawRecordName (name1, name1Length, name1Width, name2, name2Length, name2Width,
  240.          horizontalSpace, &x, y, shortenedFieldWidth, fieldSeparatorWidth, false,
  241.          name1HasPriority || !vars->sortByCompany);
  242.       
  243.       // Remember how much width was actually used for field1
  244.       field1Width = x - bounds->topLeft.x;
  245.       }
  246.    else if (vars->params->field1 == addrLookupListPhone)
  247.       {
  248.       if (field1Width > horizontalSpace)
  249.          {
  250.          everythingFits = false;
  251.          field1Width = horizontalSpace - phoneLabelWidth - 1 - shortenedFieldWidth;
  252.  
  253.          FntCharsInWidth(field1, &field1Width, &field1Length, &ignored);
  254.          }
  255.       else
  256.          {
  257.          everythingFits = true;
  258.          field1Width -= phoneLabelWidth + 1;
  259.          }
  260.       
  261.       x = bounds->topLeft.x;
  262.       if (!everythingFits)
  263.          x -= shortenedFieldWidth;
  264.       WinDrawChars(field1, field1Length, x, y);
  265.       x = bounds->topLeft.x + field1Width;
  266.       
  267.       if (!everythingFits)
  268.          {
  269.          WinDrawChars(shortenedFieldString, shortenedFieldLength, 
  270.             x, y);
  271.          x += shortenedFieldWidth;
  272.          }
  273.  
  274.       // Draw the first letter of the phone field label
  275.       phoneLabel = GetPhoneLabel(record, firstPhoneField + 
  276.          record->options.phones.displayPhoneForList);
  277.       WinDrawChars (&phoneLabelLetters[phoneLabel], 1,
  278.          x + ((phoneLabelWidth - (FntCharWidth(phoneLabelLetters[phoneLabel]) - 1)) >> 1), //lint !e702
  279.          y);
  280.       
  281.       // Remember how much width was actually used for field1
  282.       field1Width = x + phoneLabelWidth - bounds->topLeft.x;
  283.       }
  284.    else if (vars->params->field1 != addrLookupNoField)
  285.       {
  286.       if (field1Width > horizontalSpace)
  287.          {
  288.          everythingFits = false;
  289.          field1Width = horizontalSpace - shortenedFieldWidth;
  290.          FntCharsInWidth(field1, &field1Width, &field1Length, &ignored);//lint !e64
  291.          }
  292.       else
  293.          {
  294.          everythingFits = true;
  295.          }
  296.          
  297.       x = bounds->topLeft.x;
  298.       WinDrawChars(field1, field1Length, x, y);
  299.       x += field1Width;
  300.          
  301.       if (!everythingFits)
  302.          {
  303.          WinDrawChars(shortenedFieldString, shortenedFieldLength, x, y);
  304.          x += shortenedFieldWidth;
  305.          }
  306.       
  307.       // Remember how much width was actually used for field1
  308.       field1Width = x - bounds->topLeft.x;
  309.       }
  310.    
  311.    
  312.    // Find out how much room the right side can use
  313.    horizontalSpace = bounds->extent.x - field1Width - spaceBetweenNamesAndPhoneNumbers;
  314.    x = bounds->topLeft.x + bounds->extent.x - field1Width;
  315.  
  316.    // Now draw the right side.
  317.    if (vars->params->field2 == addrLookupSortField)
  318.       {
  319.       DrawRecordName (name1, name1Length, name1Width, name2, name2Length, name2Width,
  320.          horizontalSpace, &x, y, shortenedFieldWidth, fieldSeparatorWidth, false,
  321.          name1HasPriority || !vars->sortByCompany);
  322.       }
  323.    else if (vars->params->field2 == addrLookupListPhone)
  324.       {
  325.       if (field2Width > horizontalSpace)
  326.          {
  327.          everythingFits = false;
  328.          field2Width = horizontalSpace - phoneLabelWidth - 1 - shortenedFieldWidth;
  329.  
  330.          FntCharsInWidth(field2, &field2Width, &field2Length, &ignored);//lint !e64
  331.          }
  332.       else
  333.          {
  334.          everythingFits = true;
  335.          field2Width -= phoneLabelWidth + 1;
  336.          }
  337.  
  338.       
  339.       x = bounds->topLeft.x + (bounds->extent.x - 1) - field2Width - phoneLabelWidth - 1;
  340.       if (!everythingFits)
  341.          x -= shortenedFieldWidth;
  342.       WinDrawChars(field2, field2Length, x, y);
  343.          
  344.       if (!everythingFits)
  345.          {
  346.          WinDrawChars(shortenedFieldString, shortenedFieldLength, 
  347.             bounds->topLeft.x + (bounds->extent.x - 1) - shortenedFieldWidth - 
  348.             phoneLabelWidth - 1, y);
  349.          }
  350.  
  351.       // Draw the first letter of the phone field label
  352.       phoneLabel = GetPhoneLabel(record, firstPhoneField + 
  353.          record->options.phones.displayPhoneForList);
  354.       WinDrawChars (&phoneLabelLetters[phoneLabel], 1,
  355.          bounds->topLeft.x + (bounds->extent.x - 1) - phoneLabelWidth + 
  356.          ((phoneLabelWidth - (FntCharWidth(phoneLabelLetters[phoneLabel]) - 1)) >> 1), //lint !e702
  357.          y);
  358.       }
  359.    else if (vars->params->field2 != addrLookupNoField)
  360.       {
  361.       if (field2Width > horizontalSpace)
  362.          {
  363.          everythingFits = false;
  364.          field2Width = horizontalSpace - shortenedFieldWidth;
  365.          FntCharsInWidth(field2, &field2Width, &field2Length, &ignored);//lint !e64
  366.          }
  367.       else
  368.          {
  369.          everythingFits = true;
  370.          }
  371.          
  372.       x = bounds->topLeft.x + bounds->extent.x - field2Width;
  373.       if (!everythingFits)
  374.          x -= shortenedFieldWidth;
  375.       WinDrawChars(field2, field2Length, x, y);
  376.          
  377.       if (!everythingFits)
  378.          {
  379.          WinDrawChars(shortenedFieldString, shortenedFieldLength, 
  380.             bounds->topLeft.x + bounds->extent.x - shortenedFieldWidth, y);
  381.          }
  382.       }
  383.    
  384.    
  385.    
  386. }
  387.  
  388.  
  389. /***********************************************************************
  390.  *
  391.  * FUNCTION:    LookupViewDrawRecord
  392.  *
  393.  * DESCRIPTION: This routine draws an address book record.  It is called as
  394.  *              a callback routine by the table object.
  395.  *
  396.  * PARAMETERS:  table  - pointer to the address list table
  397.  *              row    - row number, in the table, of the item to draw
  398.  *              column - column number, in the table, of the item to draw
  399.  *              bounds - bounds of the draw region
  400.  *
  401.  * RETURNED:    nothing
  402.  *
  403.  * REVISION HISTORY:
  404.  *         Name   Date      Description
  405.  *         ----   ----      -----------
  406.  *         Roger   7/9/96   Initial Revision
  407.  *
  408.  ***********************************************************************/
  409.  
  410. static void LookupViewDrawRecord (VoidPtr table, Word row, Word column, 
  411.    RectanglePtr bounds)
  412. {
  413.    Word recordNum;
  414.    Word fieldNum;
  415.    Err error;
  416.    AddrDBRecordType record;
  417.    Handle recordH;
  418.    LookupVariablesPtr vars;
  419.  
  420.  
  421.    // Get the record number that corresponds to the table item to draw.
  422.    // The record number is stored in the "intValue" field of the item.
  423.    // 
  424.    recordNum = TblGetRowID (table, row);
  425.    vars = (LookupVariablesPtr) TblGetRowData(table, row);
  426.  
  427.    error = AddrGetRecord (vars->dbP, recordNum, &record, &recordH);
  428.    ErrNonFatalDisplayIf ((error), "Record not found");
  429.    if (error) return;
  430.  
  431.    fieldNum = TblGetItemInt(table, row, column);
  432.    
  433.     FntSetFont (stdFont);
  434.  
  435.    LookupDrawRecordFields (vars, &record, fieldNum, bounds, 
  436.       vars->phoneLabelLetters);
  437.  
  438.    MemHandleUnlock(recordH);
  439. }
  440.  
  441.  
  442. /***********************************************************************
  443.  *
  444.  * FUNCTION:    LookupViewUpdateScrollButtons
  445.  *
  446.  * DESCRIPTION: Show or hide the list view scroll buttons.
  447.  *
  448.  * PARAMETERS:  vars - variables used by the lookup code.
  449.  *
  450.  * RETURNED:    nothing
  451.  *
  452.  * REVISION HISTORY:
  453.  *         Name   Date      Description
  454.  *         ----   ----      -----------
  455.  *         Roger   7/9/96   Initial Revision
  456.  *
  457.  ***********************************************************************/
  458. static void LookupViewUpdateScrollButtons (LookupVariablesPtr vars)
  459. {
  460.    SWord   row;
  461.    Word upIndex;
  462.    Word downIndex;
  463.    Word recordNum;
  464.    Word phoneNum;
  465.    Boolean scrollableUp;
  466.    Boolean scrollableDown;
  467.    TablePtr table;
  468.  
  469.  
  470.    // Update the buttons that scroll the list.
  471.    //
  472.    // If the first record displayed is not the fist record in the category,
  473.    // enable the up scroller.
  474.    recordNum = vars->topVisibleRecord;
  475.    phoneNum = vars->topVisibleRecordPhone;
  476.    scrollableUp = AddrLookupSeekRecord (vars->dbP, &recordNum, &phoneNum, 1, dmSeekBackward,
  477.       vars->params->field1, vars->params->field2, vars->lookupFieldMap);
  478.  
  479.  
  480.    // Find the record in the last row of the table
  481.    table = GetObjectPtr (LookupTable);
  482.    for (row = TblGetNumberOfRows (table) - 1; row >= 0; row--)
  483.       {
  484.       // Make the row usable.
  485.       if (TblRowUsable (table, row))
  486.          {
  487.          recordNum = TblGetRowID (table, row);
  488.          phoneNum = TblGetItemInt (table, row, 0);
  489.          break;
  490.          }
  491.       }
  492.  
  493.  
  494.    // If the last record displayed is not the last record in the category,
  495.    // enable the down scroller.
  496.    scrollableDown = AddrLookupSeekRecord (vars->dbP, &recordNum, &phoneNum, 1, dmSeekForward,
  497.       vars->params->field1, vars->params->field2, vars->lookupFieldMap);
  498.  
  499.  
  500.    // Update the scroll button.
  501.    upIndex = FrmGetObjectIndex (vars->frm, LookupUpButton);
  502.    downIndex = FrmGetObjectIndex (vars->frm, LookupDownButton);
  503.    FrmUpdateScrollers (vars->frm, upIndex, downIndex, scrollableUp, scrollableDown);
  504.  
  505. }
  506.  
  507.  
  508. /***********************************************************************
  509.  *
  510.  * FUNCTION:    LookupLoadTable
  511.  *
  512.  * DESCRIPTION: This routine loads address book database records into
  513.  *              the lookup view form.  Note that the phone field may
  514.  *                be set to the first or last field if it isn't field1 or
  515.  *                field2 and the table is loaded either forward or backward.
  516.  *                So ignore it if phone are not being displayed.
  517.  *
  518.  * PARAMETERS:  vars - variables used by the lookup code.
  519.  *
  520.  * RETURNED:    nothing
  521.  *
  522.  * REVISION HISTORY:
  523.  *         Name   Date      Description
  524.  *         ----   ----      -----------
  525.  *         Roger   7/9/96   Initial Revision
  526.  *
  527.  ***********************************************************************/
  528. static void LookupLoadTable (LookupVariablesPtr vars)
  529. {
  530.    Word      row;
  531.    Word      numRows;
  532.    Word      recordNum;
  533.    Word      phoneNum;
  534.    TablePtr table;
  535.    Boolean searchPhones;
  536.  
  537.    
  538.    // For each row in the table, store the record number as the row id.
  539.    table = GetObjectPtr (LookupTable);
  540.  
  541.  
  542.    // Make sure we haven't scrolled too far down the list of records
  543.    // leaving blank lines in the table.
  544.  
  545.    // Try going forward to the last record that should be visible
  546.    numRows = TblGetNumberOfRows (table);
  547.    recordNum = vars->topVisibleRecord;
  548.    phoneNum = vars->topVisibleRecordPhone;
  549.    if (!AddrLookupSeekRecord (vars->dbP, &recordNum, &phoneNum, numRows - 1, dmSeekForward,
  550.       vars->params->field1, vars->params->field2, vars->lookupFieldMap))
  551.       {
  552.       // We have at least one line without a record.  Fix it.
  553.       // Try going backwards one page from the last record
  554.       vars->topVisibleRecord = dmMaxRecordIndex;
  555.       vars->topVisibleRecordPhone = numPhoneFields - 1;
  556.       if (!AddrLookupSeekRecord (vars->dbP, &vars->topVisibleRecord, &vars->topVisibleRecordPhone, 
  557.          numRows - 1, dmSeekBackward, vars->params->field1, vars->params->field2, 
  558.          vars->lookupFieldMap))
  559.          {
  560.          // Not enough records to fill one page.  Start with the first record
  561.          vars->topVisibleRecord = 0;
  562.          vars->topVisibleRecordPhone = 0;
  563.          AddrLookupSeekRecord (vars->dbP, &vars->topVisibleRecord, &vars->topVisibleRecordPhone,
  564.             0, dmSeekForward,   vars->params->field1, vars->params->field2, 
  565.             vars->lookupFieldMap);
  566.          }
  567.       }
  568.  
  569.  
  570.  
  571.    numRows = TblGetNumberOfRows (table);
  572.    recordNum = vars->topVisibleRecord;
  573.    phoneNum = vars->topVisibleRecordPhone;
  574.    searchPhones = IsPhoneLookupField(vars->params->field1) ||
  575.       IsPhoneLookupField(vars->params->field2);
  576.  
  577.    for (row = 0; row < numRows; row++)
  578.       {
  579.       if ( ! AddrLookupSeekRecord (vars->dbP, &recordNum, &phoneNum, 0, dmSeekForward,
  580.          vars->params->field1, vars->params->field2, vars->lookupFieldMap))
  581.          break;
  582.  
  583.       // Make the row usable.
  584.       TblSetRowUsable (table, row, true);
  585.  
  586.       // Mark the row invalid so that it will draw when we call the 
  587.       // draw routine.
  588.       TblMarkRowInvalid (table, row);
  589.  
  590.       // Store the record number as the row id.
  591.       TblSetRowID (table, row, recordNum);
  592.       
  593.       // Store a pointer to vars for the callback.  Ideally the table would
  594.       // have one copy of this information but it doesn't so we keep
  595.       // a pointer to vars in every row.
  596.       TblSetRowData(table, row, (ULong) vars);
  597.       
  598.       // Store the field used for a phone in the item int.
  599.       // We need to do this because some records have multiple
  600.       // occurances when they have multiple identical phone types
  601.       // (i.e. two email address).  The type is AddressField.
  602.       // Remember that we allow only one of the two fields to be
  603.       // a phone field.
  604.       TblSetItemInt (table, row, 0, firstPhoneField + phoneNum);
  605.  
  606.       if (searchPhones)
  607.          {
  608.          phoneNum++;
  609.          if (phoneNum >= numPhoneFields)
  610.             {
  611.             phoneNum = 0;
  612.             recordNum++;
  613.             }
  614.          }
  615.       else
  616.          recordNum++;
  617.       }
  618.    
  619.  
  620.    // Hide the item that don't have any data.
  621.    while (row < numRows)
  622.       {      
  623.       TblSetRowUsable (table, row, false);
  624.       row++;
  625.       }
  626.  
  627.    TblUnhighlightSelection(table);
  628.  
  629.    LookupViewUpdateScrollButtons(vars);
  630. }
  631.  
  632.  
  633. /***********************************************************************
  634.  *
  635.  * FUNCTION:    LookupViewScroll
  636.  *
  637.  * DESCRIPTION: This routine scrolls the list of names and phone numbers 
  638.  *              in the direction specified.
  639.  *
  640.  * PARAMETERS:  vars - variables used by the lookup code.
  641.  *              direction - up or dowm
  642.  *              oneLine   - if true the list is scroll by a single line,
  643.  *                          if false the list is scroll by a full screen.
  644.  *
  645.  * RETURNED:    nothing
  646.  *
  647.  * REVISION HISTORY:
  648.  *         Name   Date      Description
  649.  *         ----   ----      -----------
  650.  *         Roger   7/9/96   Initial Revision
  651.  *
  652.  ***********************************************************************/
  653. static void LookupViewScroll (LookupVariablesPtr vars, DirectionType direction, 
  654.    Boolean oneLine)
  655. {
  656.    TablePtr table;
  657.    Word rowsInTable;
  658.    UInt newTopVisibleRecord;
  659.    UInt newTopVisibleRecordPhone;
  660.  
  661.    
  662.    table = GetObjectPtr (LookupTable);
  663.    rowsInTable = TblGetNumberOfRows (table);
  664.    newTopVisibleRecord = vars->topVisibleRecord;
  665.    newTopVisibleRecordPhone = vars->topVisibleRecordPhone;
  666.    
  667.  
  668.    // Scroll the table down.
  669.    if (direction == down)
  670.       {
  671.       // Scroll down a single line.  If we can't scroll down a line
  672.       // then the scroll down arrow would not have been displayed.
  673.       if (oneLine)
  674.          {
  675.          AddrLookupSeekRecord (vars->dbP, &newTopVisibleRecord, &newTopVisibleRecordPhone, 
  676.             1, dmSeekForward, vars->params->field1, vars->params->field2, vars->lookupFieldMap);
  677.          ErrNonFatalDisplayIf (DmGetLastErr(), "Error scrolling");
  678.          }
  679.  
  680.       // Scroll down a page (less one row).
  681.       else
  682.          {
  683.          // Try going forward one page
  684.          if (!AddrLookupSeekRecord (vars->dbP, &newTopVisibleRecord, &newTopVisibleRecordPhone, 
  685.             rowsInTable - 1, dmSeekForward, vars->params->field1, vars->params->field2, 
  686.             vars->lookupFieldMap))
  687.             {
  688.             // Try going backwards one page from the last record
  689.             newTopVisibleRecord = dmMaxRecordIndex;
  690.             newTopVisibleRecordPhone = numPhoneFields - 1;
  691.             if (!AddrLookupSeekRecord (vars->dbP, &newTopVisibleRecord, &newTopVisibleRecordPhone, 
  692.                rowsInTable - 1, dmSeekBackward, vars->params->field1, 
  693.                vars->params->field2, vars->lookupFieldMap))
  694.                {
  695.                // Not enough records to fill one page.  Start with the first record
  696.                newTopVisibleRecord = 0;
  697.                newTopVisibleRecordPhone = 0;
  698.                AddrLookupSeekRecord (vars->dbP, &newTopVisibleRecord, &newTopVisibleRecordPhone, 
  699.                   0, dmSeekForward, vars->params->field1, vars->params->field2, 
  700.                   vars->lookupFieldMap);
  701.                }
  702.             }
  703.          }
  704.       }
  705.  
  706.  
  707.  
  708.    // Scroll the table up.
  709.    else
  710.       {
  711.       // Scroll up a single line
  712.       if (oneLine)
  713.          {
  714.          AddrLookupSeekRecord (vars->dbP, &newTopVisibleRecord, &newTopVisibleRecordPhone, 1, 
  715.             dmSeekBackward, vars->params->field1, vars->params->field2, vars->lookupFieldMap);
  716.          ErrNonFatalDisplayIf (DmGetLastErr(), "Error scrolling");
  717.          }
  718.  
  719.       // Scroll up a page (less one row).
  720.       else
  721.          {
  722.          if (!AddrLookupSeekRecord (vars->dbP, &newTopVisibleRecord, &newTopVisibleRecordPhone, 
  723.             rowsInTable - 1, dmSeekBackward, vars->params->field1, vars->params->field2, 
  724.             vars->lookupFieldMap))
  725.             {
  726.             // Not enough records to fill one page.  Start with the first record
  727.             newTopVisibleRecord = 0;
  728.             newTopVisibleRecordPhone = 0;
  729.             AddrLookupSeekRecord (vars->dbP, &newTopVisibleRecord, &newTopVisibleRecordPhone, 
  730.                0, dmSeekForward, vars->params->field1, vars->params->field2, 
  731.                vars->lookupFieldMap);
  732.             }
  733.          }
  734.       }
  735.  
  736.  
  737.    // Avoid redraw if no change
  738.    if (vars->topVisibleRecord != newTopVisibleRecord ||
  739.       vars->topVisibleRecordPhone != newTopVisibleRecordPhone)
  740.       {
  741.       vars->topVisibleRecord = newTopVisibleRecord;
  742.       vars->topVisibleRecordPhone = newTopVisibleRecordPhone;
  743.       LookupLoadTable(vars);
  744.       TblRedrawTable(table);
  745.       }
  746. }
  747.  
  748.  
  749. /***********************************************************************
  750.  *
  751.  * FUNCTION:    LookupViewSelectRecord
  752.  *
  753.  * DESCRIPTION: Selects (highlights) a record on the table, scrolling
  754.  *              the record if neccessary.  Also sets the CurrentRecord.
  755.  *
  756.  * PARAMETERS:  vars - variables used by the lookup code.
  757.  *              recordNum - record to select
  758.  *              phoneNum - phone in record to select
  759.  *                      
  760.  * RETURNED:    nothing
  761.  *
  762.  * REVISION HISTORY:
  763.  *         Name   Date      Description
  764.  *         ----   ----      -----------
  765.  *         Roger   7/9/96   Initial Revision
  766.  *
  767.  ***********************************************************************/
  768. static void LookupViewSelectRecord (LookupVariablesPtr vars, 
  769.    UInt recordNum, UInt phoneNum)
  770. {
  771.    UInt row, column;
  772.    TablePtr tableP;
  773.    UInt attr;
  774.    Boolean recordFound;
  775.    Boolean searchPhones;
  776.  
  777.  
  778.    ErrFatalDisplayIf (recordNum >= DmNumRecords(vars->dbP), "Record outside AddrDB");
  779.    ErrFatalDisplayIf (phoneNum >= numPhoneFields, "Phone outside legal range");
  780.  
  781.  
  782.    tableP = GetObjectPtr (LookupTable);
  783.  
  784.  
  785.    // Don't change anything if the same record is selected
  786.    if (TblGetSelection(tableP, &row, &column) &&
  787.       recordNum == TblGetRowID (tableP, row) &&
  788.       phoneNum == TblGetItemInt (tableP, row, 0))
  789.       {
  790.       return;
  791.       }
  792.       
  793.  
  794.    searchPhones = IsPhoneLookupField(vars->params->field1) ||
  795.       IsPhoneLookupField(vars->params->field2);
  796.  
  797.    // See if the record is displayed by one of the rows in the table
  798.    // A while is used because if TblFindRowID fails we need to
  799.    // call it again to find the row in the reloaded table.
  800.    while (true)
  801.       {
  802.       recordFound = false;
  803.       if (TblFindRowID(tableP, recordNum, &row))
  804.          {
  805.          if (searchPhones)
  806.             {
  807.             for (; row < TblGetNumberOfRows(tableP); row++)
  808.                {
  809.                if (TblRowUsable(tableP, row))
  810.                   {
  811.                   if (phoneNum == TblGetItemInt(tableP, row, 0) - firstPhoneField)
  812.                      {
  813.                      recordFound = true;
  814.                      break; // match found
  815.                      }
  816.                   }            
  817.                }
  818.             }
  819.          else
  820.             {
  821.             recordFound = true;
  822.             break; // match found
  823.             }
  824.          }
  825.       
  826.       // If the record is found in the existing table stop
  827.       // and go select it.  Otherwise position that table to
  828.       // start with the record, reload the table, and look
  829.       // for it again.
  830.       if (recordFound)
  831.          break;
  832.          
  833.       if (vars->hideSecretRecords)
  834.          {
  835.          // If the record is hidden stop trying to show it.
  836.          DmRecordInfo(vars->dbP, recordNum, &attr, NULL, NULL);
  837.          if (attr & dmRecAttrSecret)
  838.             {
  839.             return;
  840.             }
  841.          }
  842.             
  843.       // Scroll the view down placing the item
  844.       // on the top row
  845.       vars->topVisibleRecord = recordNum;
  846.       vars->topVisibleRecordPhone = phoneNum;
  847.  
  848.       LookupLoadTable(vars);
  849.       TblRedrawTable(tableP);
  850.       }
  851.  
  852.    
  853.    // Select the item
  854.    TblSelectItem (tableP, row, 0);
  855.    
  856.    vars->currentRecord = recordNum;
  857.    vars->currentPhone = phoneNum;
  858. }   
  859.    
  860.  
  861. /***********************************************************************
  862.  *
  863.  * FUNCTION:    LookupViewLookupString
  864.  *
  865.  * DESCRIPTION: Adds a character to LookupLookupField, looks up the 
  866.  * string in the database and selects the item that matches.
  867.  *
  868.  * PARAMETERS:  vars - variables used by the lookup code.
  869.  *              event - EventPtr containing character to add to LookupLookupField
  870.  *                        or NULL to use the text there
  871.  *                      
  872.  * RETURNED:    true if the field handled the event
  873.  *
  874.  * REVISION HISTORY:
  875.  *         Name   Date      Description
  876.  *         ----   ----      -----------
  877.  *         Roger   7/9/96   Initial Revision
  878.  *
  879.  ***********************************************************************/
  880. static Boolean LookupViewLookupString (LookupVariablesPtr vars, 
  881.    EventPtr event)
  882. {
  883.    FormPtr frm;
  884.    UInt fldIndex;
  885.    FieldPtr fldP;
  886.    CharPtr fldTextP;
  887.    TablePtr tableP;
  888.    UInt foundRecord;
  889.    UInt foundRecordPhone;
  890.    Boolean completeMatch;
  891.    Boolean uniqueMatch;
  892.    Int length;
  893.    
  894.             
  895.    frm = FrmGetActiveForm();
  896.    fldIndex = FrmGetObjectIndex(frm, LookupLookupField);
  897.    FrmSetFocus(frm, fldIndex);
  898.    fldP = FrmGetObjectPtr (frm, fldIndex);
  899.  
  900.    
  901.    if (event == NULL ||
  902.       FldHandleEvent (fldP, event))
  903.       {
  904.       fldTextP = FldGetTextPtr(fldP);
  905.       tableP = FrmGetObjectPtr (frm, FrmGetObjectIndex(frm, LookupTable));
  906.  
  907.         foundRecordPhone = 0;
  908.       if (!AddrLookupLookupString(vars->dbP, fldTextP, vars->sortByCompany, 
  909.          vars->params->field1, vars->params->field2, &foundRecord, &foundRecordPhone, 
  910.          vars->lookupFieldMap, &completeMatch, &uniqueMatch))  //foundRecordPhone
  911.          {
  912.          // If the user deleted the lookup text remove the
  913.          // highlight.
  914.          TblUnhighlightSelection(tableP);
  915.          }
  916.       else
  917.          {
  918.          LookupViewSelectRecord(vars, foundRecord, foundRecordPhone);
  919.          }
  920.       
  921.       
  922.       if (!completeMatch)
  923.          {
  924.          // Delete the last character added.
  925.          length = FldGetTextLength(fldP);
  926.          FldDelete(fldP, length - 1, length);
  927.          
  928.          SndPlaySystemSound (sndError);
  929.          }
  930.          
  931.       return true;
  932.       }
  933.  
  934.    // Event not handled
  935.    return false;
  936.    
  937. }
  938.  
  939.  
  940. /***********************************************************************
  941.  *
  942.  * FUNCTION:    LookupClearLookupString
  943.  *
  944.  * DESCRIPTION: Clears the LookupLookupField.  Does not unhighlight the item.
  945.  *
  946.  * PARAMETERS:  nothing
  947.  *                      
  948.  * RETURNED:    nothing
  949.  *
  950.  * REVISION HISTORY:
  951.  *         Name   Date      Description
  952.  *         ----   ----      -----------
  953.  *         Roger   7/9/96   Initial Revision
  954.  *
  955.  ***********************************************************************/
  956. static void LookupClearLookupString ()
  957. {
  958.    FormPtr frm;
  959.    UInt fldIndex;
  960.    FieldPtr fldP;
  961.    Int length;
  962.  
  963.  
  964.    frm = FrmGetActiveForm();
  965. //   FrmSetFocus(frm, noFocus);
  966.    fldIndex = FrmGetObjectIndex(frm, LookupLookupField);
  967.    fldP = FrmGetObjectPtr (frm, fldIndex);
  968.  
  969.    length = FldGetTextLength(fldP);
  970.    if (length > 0)
  971.       FldDelete(fldP, 0, length);
  972. }
  973.  
  974.  
  975. /***********************************************************************
  976.  *
  977.  * FUNCTION:    LookupViewInit
  978.  *
  979.  * DESCRIPTION: This routine initializes the "Lookup View" of the 
  980.  *              Address application.
  981.  *
  982.  * PARAMETERS:  vars - variables used by the lookup code.
  983.  *
  984.  * RETURNED:    true if the event has handle and should not be passed
  985.  *              to a higher level handler.
  986.  *
  987.  * REVISION HISTORY:
  988.  *         Name   Date      Description
  989.  *         ----   ----      -----------
  990.  *         Roger   7/9/96   Initial Revision
  991.  *
  992.  ***********************************************************************/
  993. static void LookupViewInit (LookupVariablesPtr vars)
  994. {
  995.    Word row;
  996.    Word rowsInTable;
  997.    TablePtr table;
  998.    RectangleType bounds;
  999.    
  1000.    
  1001.    // Initialize the address list table.
  1002.    table = FrmGetObjectPtr (vars->frm, FrmGetObjectIndex (vars->frm, LookupTable));
  1003.    rowsInTable = TblGetNumberOfRows (table);
  1004.    for (row = 0; row < rowsInTable; row++)
  1005.       {      
  1006.       TblSetItemStyle (table, row, field1Column, customTableItem);
  1007.       TblSetRowUsable (table, row, false);
  1008.       }
  1009.  
  1010.    TblSetColumnUsable (table, 0, true);
  1011.  
  1012.  
  1013.    // Set the callback routine that will draw the records.
  1014.    TblSetCustomDrawProcedure (table, field1Column, LookupViewDrawRecord);
  1015.  
  1016.  
  1017.    // Load records into the address list.
  1018.    LookupLoadTable (vars);
  1019.  
  1020.  
  1021.    // Turn on the cursor in the lookup field.
  1022. //   FrmSetFocus(vars->frm, FrmGetObjectIndex (vars->frm, LookupLookupField));
  1023.  
  1024.    // Set the bounds of the title, so that the title will draw across the 
  1025.    // entire display.
  1026.    bounds.topLeft.x = 0;
  1027.    bounds.topLeft.y = 0;
  1028.    WinGetWindowExtent (&bounds.extent.x, &bounds.extent.y);
  1029.    FrmSetObjectBounds (vars->frm, 0, &bounds);
  1030.    
  1031.    // Respond to an empty lookup field.
  1032.    vars->ignoreEmptyLookupField = false;
  1033. }
  1034.  
  1035.  
  1036. /***********************************************************************
  1037.  *
  1038.  * FUNCTION:    LookupViewUseSelection
  1039.  *
  1040.  * DESCRIPTION: Use the record currently selected.
  1041.  *
  1042.  * PARAMETERS:  vars - variables used by the lookup code.
  1043.  *
  1044.  * RETURNED:    true if a record was selected and false if not.
  1045.  *
  1046.  * REVISION HISTORY:
  1047.  *         Name   Date      Description
  1048.  *         ----   ----      -----------
  1049.  *         Roger   11/11/96   Initial Revision
  1050.  *
  1051.  ***********************************************************************/
  1052. static Boolean LookupViewUseSelection (LookupVariablesPtr vars)
  1053. {
  1054.    TablePtr table;
  1055.    UInt row;
  1056.    UInt column;
  1057.    
  1058.    
  1059.    // If a row is selected return the record number else
  1060.    // return noRecord.
  1061.    table = FrmGetObjectPtr (vars->frm, FrmGetObjectIndex (vars->frm, LookupTable));
  1062.    if (TblGetSelection (table, &row, &column))
  1063.       {
  1064.       vars->currentRecord = TblGetRowID(table, row);
  1065.       vars->currentPhone = TblGetItemInt(table, row, column);
  1066.       return true;
  1067.       }
  1068.    
  1069.    return false;
  1070. }
  1071.  
  1072.  
  1073. /****************************************************************************
  1074.  *
  1075.  * DESCRIPTION: This is the event handler for the "Lookup View"
  1076.  *              of the Address Book application.
  1077.  *
  1078.  * PARAMETERS:  vars - variables used by the lookup code.
  1079.  *
  1080.  * RETURNED:    true if a record was selected and false if not.
  1081.  *
  1082.  * REVISION HISTORY:
  1083.  *         Name   Date      Description
  1084.  *         ----   ----      -----------
  1085.  *         Roger   7/8/96   Initial Revision
  1086.  *
  1087.  ***********************************************************************/
  1088. static Boolean LookupViewHandleEvent (LookupVariablesPtr vars)
  1089. {
  1090.    EventType event;
  1091.    Boolean handled;
  1092.  
  1093.  
  1094.    while (true)
  1095.       {
  1096.       EvtGetEvent (&event, evtWaitForever);
  1097.       
  1098.       // Cancel if something is going to switch apps.
  1099.       if (event.eType == keyDownEvent && 
  1100.           event.data.keyDown.chr == findChr)
  1101.           {
  1102.             EvtAddEventToQueue(&event);
  1103.             return false;
  1104.           }
  1105.           
  1106.       if (SysHandleEvent (&event))
  1107.          continue;
  1108.       
  1109.       
  1110.       handled = false;
  1111.       
  1112.       // Clear the lookup string because the user is selecting an item.
  1113.         if (event.eType == tblSelectEvent)
  1114.           {
  1115.          // Doing the next call sends a fldChangedEvent which remove's the 
  1116.          // table's selection.  Set a flag to not handle the event.
  1117.          LookupClearLookupString ();
  1118.          vars->ignoreEmptyLookupField = true;
  1119.          
  1120.          handled = true;
  1121.          }
  1122.         
  1123.         
  1124.         else if (event.eType == ctlSelectEvent)
  1125.           {
  1126.           if (event.data.ctlSelect.controlID == LookupPasteButton)
  1127.               return LookupViewUseSelection(vars);
  1128.           
  1129.           else if (event.data.ctlSelect.controlID == LookupCancelButton)
  1130.                 return false;
  1131.             }
  1132.         
  1133.         
  1134.         else if (event.eType == ctlRepeatEvent)
  1135.           {
  1136.           if (event.data.ctlRepeat.controlID == LookupUpButton)
  1137.               {
  1138.                 LookupViewScroll (vars, up, false);
  1139.                 LookupClearLookupString ();
  1140.                 // leave unhandled so the buttons can repeat
  1141.                 }
  1142.             
  1143.             else if (event.data.ctlRepeat.controlID == LookupDownButton)
  1144.                 {
  1145.                 LookupViewScroll (vars, down, false);
  1146.                 LookupClearLookupString ();
  1147.                 // leave unhandled so the buttons can repeat
  1148.                 }
  1149.             }
  1150.         
  1151.         
  1152.         else if (event.eType == keyDownEvent)
  1153.           {
  1154.             if (TxtCharIsHardKey(event.data.keyDown.modifiers, event.data.keyDown.chr))
  1155.                 {
  1156.                 // SysHandleEvent saw these keys and is now switching apps. 
  1157.                 // Leave the Lookup function.
  1158.                 return false;
  1159.                 }
  1160.             
  1161.          switch (event.data.keyDown.chr)
  1162.              {
  1163.             case pageUpChr:
  1164.                LookupViewScroll (vars, up, false);
  1165.                LookupClearLookupString ();
  1166.                handled = true;
  1167.                break;
  1168.                
  1169.             case pageDownChr:
  1170.                LookupViewScroll (vars, down, false);
  1171.                LookupClearLookupString ();
  1172.                handled = true;
  1173.                break;
  1174.  
  1175.             case linefeedChr:
  1176.                return LookupViewUseSelection(vars);
  1177.  
  1178.  
  1179.             default:
  1180.                handled = LookupViewLookupString(vars, &event);
  1181.                
  1182.                // If the field becomes empty, handle it.
  1183.                vars->ignoreEmptyLookupField = false;
  1184.                break;
  1185.              }
  1186.         }
  1187.         
  1188.         
  1189.         else if (event.eType == fldChangedEvent)
  1190.           {
  1191.             if (!(vars->ignoreEmptyLookupField &&
  1192.                FldGetTextLength(FrmGetObjectPtr (vars->frm, 
  1193.                   FrmGetObjectIndex(vars->frm, LookupLookupField))) == 0))
  1194.                {
  1195.                LookupViewLookupString(vars, NULL);
  1196.                }
  1197.  
  1198.             vars->ignoreEmptyLookupField = false;
  1199.             handled = true;
  1200.             }
  1201.         
  1202.         
  1203.         else if (event.eType == appStopEvent)
  1204.           {
  1205.             EvtAddEventToQueue(&event);
  1206.             return false;
  1207.             }
  1208.         
  1209.         
  1210.       // Check if the form can handle the event
  1211.       if (!handled)
  1212.          FrmHandleEvent (vars->frm, &event);
  1213.       
  1214.        }
  1215. }
  1216.  
  1217.  
  1218. /***********************************************************************
  1219.  *
  1220.  * FUNCTION:    LookupFindPhoneField
  1221.  *
  1222.  * DESCRIPTION: Find a phone field from the record.  The first match
  1223.  * is used unless it's one of the fields displayed.  In that case the
  1224.  * field displayed is used.
  1225.  *
  1226.  * PARAMETERS:  vars - variables used by the lookup code.
  1227.  *                recordP - the record to find the field in
  1228.  *                lookupField - the phone field to lookup
  1229.  *                phoneNum - the phone field in the record that matches
  1230.  *                           (used when field 1 or field2 is a phone field).
  1231.  *
  1232.  * RETURNED:    The field to use or lookupNoField if none found 
  1233.  *
  1234.  * REVISION HISTORY:
  1235.  *         Name   Date      Description
  1236.  *         ----   ----      -----------
  1237.  *         Roger   7/18/96   Initial Revision
  1238.  *
  1239.  ***********************************************************************/
  1240. static AddressFields LookupFindPhoneField (LookupVariablesPtr vars, 
  1241.    AddrDBRecordPtr recordP, AddressLookupFields lookupField, 
  1242.    UInt phoneNum)
  1243. {
  1244.    int index;
  1245.    int phoneType;
  1246.    
  1247.    
  1248.    if (vars->params->field1 == lookupField || vars->params->field2 == lookupField)
  1249.       return (AddressFields) phoneNum;
  1250.    else
  1251.       {
  1252.       phoneType = lookupField - addrLookupWork;
  1253.       
  1254.       // Scan through the phone fields looking for a phone of the right type
  1255.       // which also contains data.
  1256.       for (index = firstPhoneField; index <= lastPhoneField; index++)
  1257.          {
  1258.          if (GetPhoneLabel(recordP, index) == phoneType &&
  1259.             recordP->fields[index] != NULL)
  1260.             {
  1261.             return (AddressFields) (phone1 + index - firstPhoneField);
  1262.             }
  1263.          }
  1264.          
  1265.       }
  1266.    
  1267.    return (AddressFields) addrLookupNoField;
  1268. }
  1269.  
  1270.  
  1271. /***********************************************************************
  1272.  *
  1273.  * FUNCTION:    LookupResizeResultString
  1274.  *
  1275.  * DESCRIPTION: Resize the lookup a result string
  1276.  *
  1277.  * PARAMETERS:  resultStringP - result string
  1278.  *              newSize       - new size
  1279.  *
  1280.  * RETURNED:    pointer to the resized result of zero if the resize failed.
  1281.  *
  1282.  * REVISION HISTORY:
  1283.  *         Name   Date      Description
  1284.  *         ----   ----      -----------
  1285.  *         Art   12/11/96   Initial Revision
  1286.  *
  1287.  ***********************************************************************/
  1288. static CharPtr LookupResizeResultString (VoidHand resultStringH, Word newSize)
  1289. {
  1290.    Err err;
  1291.    
  1292.    MemHandleUnlock (resultStringH);
  1293.    
  1294.    err = MemHandleResize (resultStringH, newSize);
  1295.    if (err)
  1296.       return (0);
  1297.       
  1298.    return (MemHandleLock (resultStringH));
  1299. }
  1300.  
  1301. /***********************************************************************
  1302.  *
  1303.  * FUNCTION:    LookupCreateResultString
  1304.  *
  1305.  * DESCRIPTION: Create a result string which includes data from the record.
  1306.  *
  1307.  * PARAMETERS:  vars - variables used by the lookup code.
  1308.  *                recordNum - the record create a result string from
  1309.  *                phoneNum - the phone field in the record that matches
  1310.  *                           (used when field 1 or field2 is a phone field).
  1311.  *
  1312.  * RETURNED:    The handle to the string created or NULL.
  1313.  *                vars->params->resultStringH is also set
  1314.  *
  1315.  * REVISION HISTORY:
  1316.  *         Name   Date      Description
  1317.  *         ----   ----      -----------
  1318.  *         Roger   7/9/96   Initial Revision
  1319.  *
  1320.  ***********************************************************************/
  1321. static Handle LookupCreateResultString (LookupVariablesPtr vars, 
  1322.    UInt recordNum, UInt phoneNum)
  1323. {
  1324.    CharPtr formatStringP;
  1325.    Handle resultStringH;
  1326.    CharPtr resultStringP;
  1327.    Int resultSize = 0;
  1328.    Int nextChunkSize;
  1329.    CharPtr nextFieldP;
  1330.    Int field;
  1331.    AddrDBRecordType record;
  1332.    Handle recordH;
  1333.    CharPtr fieldP;
  1334.    Err error;
  1335.    UInt separatorLength;
  1336.    UInt phoneLabel;
  1337.    
  1338.    
  1339.    // Return the record's unique ID
  1340.    DmRecordInfo(vars->dbP, recordNum, NULL, &vars->params->uniqueID, NULL);
  1341.  
  1342.    // Check if a format string was specified
  1343.    formatStringP = vars->params->formatStringP;
  1344.    if (formatStringP == NULL)
  1345.       return 0;
  1346.    
  1347.    // Allocate the string on the dynamic heap
  1348.    resultStringH = MemHandleNew(32);      // Allocate extra so there's room to grow
  1349.    if (!resultStringH)
  1350.       return 0;            // not enough memory?
  1351.    
  1352.    error = AddrGetRecord (vars->dbP, recordNum, &record, &recordH);
  1353.    ErrFatalDisplayIf ((error), "Record not found");
  1354.  
  1355.    resultStringP = MemHandleLock(resultStringH);
  1356.    while (*formatStringP != '\0')
  1357.       {
  1358.       // Copy the next chunk (the string until a '^' or '\0'
  1359.       nextFieldP = StrChr(formatStringP, '^');
  1360.       if (nextFieldP)
  1361.          nextChunkSize = nextFieldP - formatStringP;
  1362.       else
  1363.          nextChunkSize = StrLen(formatStringP);
  1364.  
  1365.       if (nextChunkSize > 0)
  1366.          {
  1367.          resultStringP = LookupResizeResultString (resultStringH, 
  1368.             resultSize + nextChunkSize);
  1369.          if (! resultStringP) goto exit;
  1370.          
  1371.          MemMove(resultStringP + resultSize, formatStringP, nextChunkSize);
  1372.          
  1373.          resultSize += nextChunkSize;
  1374.          formatStringP += nextChunkSize;
  1375.          nextChunkSize = 0;
  1376.          }
  1377.       
  1378.       // determine which field to copy next
  1379.       if (*formatStringP == '^')
  1380.          {
  1381.          formatStringP++;
  1382.          field = (AddressFields) addrLookupNoField;
  1383.          
  1384.          // Decode which field to copy next.
  1385.  
  1386.          // Remember that the strings below can't be put into a table 
  1387.          // because the lookup function runs without global variables 
  1388.          // available with which we would store the table.
  1389.          if (StrNCompare(formatStringP, "name", 4) == 0)
  1390.             {
  1391.             field = name;
  1392.             formatStringP += 4;
  1393.             }
  1394.          else if (StrNCompare(formatStringP, "first", 5) == 0)
  1395.             {
  1396.             field = firstName;
  1397.             formatStringP += 5;
  1398.             }
  1399.          else if (StrNCompare(formatStringP, "company", 7) == 0)
  1400.             {
  1401.             field = company;
  1402.             formatStringP += 7;
  1403.             }
  1404.          else if (StrNCompare(formatStringP, "address", 7) == 0)
  1405.             {
  1406.             field = address;
  1407.             formatStringP += 7;
  1408.             }
  1409.          else if (StrNCompare(formatStringP, "city", 4) == 0)
  1410.             {
  1411.             field = city;
  1412.             formatStringP += 4;
  1413.             }
  1414.          else if (StrNCompare(formatStringP, "state", 5) == 0)
  1415.             {
  1416.             field = state;
  1417.             formatStringP += 5;
  1418.             }
  1419.          else if (StrNCompare(formatStringP, "zipcode", 7) == 0)
  1420.             {
  1421.             field = zipCode;
  1422.             formatStringP += 7;
  1423.             }
  1424.          else if (StrNCompare(formatStringP, "country", 7) == 0)
  1425.             {
  1426.             field = country;
  1427.             formatStringP += 7;
  1428.             }
  1429.          else if (StrNCompare(formatStringP, "title", 5) == 0)
  1430.             {
  1431.             field = title;
  1432.             formatStringP += 5;
  1433.             }
  1434.          else if (StrNCompare(formatStringP, "custom1", 7) == 0)
  1435.             {
  1436.             field = custom1;
  1437.             formatStringP += 7;
  1438.             }
  1439.          else if (StrNCompare(formatStringP, "custom2", 7) == 0)
  1440.             {
  1441.             field = custom2;
  1442.             formatStringP += 7;
  1443.             }
  1444.          else if (StrNCompare(formatStringP, "custom3", 7) == 0)
  1445.             {
  1446.             field = custom3;
  1447.             formatStringP += 7;
  1448.             }
  1449.          else if (StrNCompare(formatStringP, "custom4", 7) == 0)
  1450.             {
  1451.             field = custom4;
  1452.             formatStringP += 7;
  1453.             }
  1454.          else if (StrNCompare(formatStringP, "work", 4) == 0)
  1455.             {
  1456.             field = LookupFindPhoneField(vars, &record, addrLookupWork, phoneNum);
  1457.             formatStringP += 4;
  1458.             }
  1459.          else if (StrNCompare(formatStringP, "home", 5) == 0)
  1460.             {
  1461.             field = LookupFindPhoneField(vars, &record, addrLookupHome, phoneNum);
  1462.             formatStringP += 5;
  1463.             }
  1464.          else if (StrNCompare(formatStringP, "fax", 5) == 0)
  1465.             {
  1466.             field = LookupFindPhoneField(vars, &record, addrLookupFax, phoneNum);
  1467.             formatStringP += 5;
  1468.             }
  1469.          else if (StrNCompare(formatStringP, "other", 5) == 0)
  1470.             {
  1471.             field = LookupFindPhoneField(vars, &record, addrLookupOther, phoneNum);
  1472.             formatStringP += 5;
  1473.             }
  1474.          else if (StrNCompare(formatStringP, "email", 5) == 0)
  1475.             {
  1476.             field = LookupFindPhoneField(vars, &record, addrLookupEmail, phoneNum);
  1477.             formatStringP += 5;
  1478.             }
  1479.          else if (StrNCompare(formatStringP, "main", 5) == 0)
  1480.             {
  1481.             field = LookupFindPhoneField(vars, &record, addrLookupMain, phoneNum);
  1482.             formatStringP += 5;
  1483.             }
  1484.          else if (StrNCompare(formatStringP, "pager", 5) == 0)
  1485.             {
  1486.             field = LookupFindPhoneField(vars, &record, addrLookupPager, phoneNum);
  1487.             formatStringP += 5;
  1488.             }
  1489.          else if (StrNCompare(formatStringP, "mobile", 5) == 0)
  1490.             {
  1491.             field = LookupFindPhoneField(vars, &record, addrLookupMobile, phoneNum);
  1492.             formatStringP += 5;
  1493.             }
  1494.          else if (StrNCompare(formatStringP, "listname", 8) == 0)
  1495.             {
  1496.             formatStringP += 8;
  1497.             separatorLength = 0;
  1498.             
  1499.             // Add the company name 
  1500.             if (vars->sortByCompany)
  1501.                {
  1502.                fieldP = record.fields[company];
  1503.                if (fieldP)
  1504.                   {
  1505.                   nextChunkSize = StrLen(fieldP);
  1506.  
  1507.                   if (nextChunkSize > 0)
  1508.                      {
  1509.                      resultStringP = LookupResizeResultString (resultStringH,
  1510.                         resultSize + nextChunkSize);
  1511.                      if (! resultStringP) goto exit;
  1512.  
  1513.                      MemMove(resultStringP + resultSize, fieldP, nextChunkSize);
  1514.                      
  1515.                      resultSize += nextChunkSize;
  1516.                      nextChunkSize = 0;
  1517.                      separatorLength = 2;
  1518.                      }
  1519.                   }
  1520.                }
  1521.             
  1522.             // Add the name field
  1523.             fieldP = record.fields[name];
  1524.             if (fieldP)
  1525.                {
  1526.                nextChunkSize = StrLen(fieldP);
  1527.  
  1528.                if (nextChunkSize > 0)
  1529.                   {
  1530.                   resultStringP = LookupResizeResultString (resultStringH, resultSize + nextChunkSize + separatorLength);
  1531.                   if (! resultStringP) goto exit;
  1532.                   if (separatorLength > 0)
  1533.                      {
  1534.                      MemMove(resultStringP + resultSize, ", ", separatorLength);
  1535.                      resultSize += separatorLength;
  1536.                      }
  1537.                   MemMove(resultStringP + resultSize, fieldP, nextChunkSize);
  1538.                   
  1539.                   resultSize += nextChunkSize;
  1540.                   nextChunkSize = 0;
  1541.                   separatorLength = 2;
  1542.                   }
  1543.                }
  1544.             
  1545.             // Add the first name field
  1546.             if (!vars->sortByCompany ||
  1547.                record.fields[company] == NULL)
  1548.                {
  1549.                fieldP = record.fields[firstName];
  1550.                if (fieldP)
  1551.                   {
  1552.                   nextChunkSize = StrLen(fieldP);
  1553.  
  1554.                   if (nextChunkSize > 0)
  1555.                      {
  1556.                      resultStringP = LookupResizeResultString (resultStringH, resultSize + nextChunkSize + separatorLength);
  1557.                      if (! resultStringP) goto exit;
  1558.                      if (separatorLength > 0)
  1559.                         {
  1560.                         MemMove(resultStringP + resultSize, ", ", separatorLength);
  1561.                         resultSize += separatorLength;
  1562.                         }
  1563.                      MemMove(resultStringP + resultSize, fieldP, nextChunkSize);
  1564.                      
  1565.                      resultSize += nextChunkSize;
  1566.                      nextChunkSize = 0;
  1567.                      }
  1568.                   }
  1569.                }
  1570.             
  1571.             // We are done adding the data requested.  Continue to the next
  1572.             // chunk
  1573.             continue;
  1574.             }
  1575.          else if (StrNCompare(formatStringP, "listphone", 9) == 0)
  1576.             {
  1577.             formatStringP += 9;
  1578.             separatorLength = 0;
  1579.             
  1580.             // Add the list phone number with a letter after it 
  1581.             fieldP = record.fields[firstPhoneField + 
  1582.                record.options.phones.displayPhoneForList];
  1583.             if (fieldP)
  1584.                {
  1585.                nextChunkSize = StrLen(fieldP);
  1586.                if (nextChunkSize > 0)
  1587.                   {
  1588.                   resultStringP = LookupResizeResultString (resultStringH, resultSize + nextChunkSize + 2);
  1589.                   if (! resultStringP) goto exit;
  1590.  
  1591.                   MemMove(resultStringP + resultSize, fieldP, nextChunkSize);
  1592.                   
  1593.                   resultSize += nextChunkSize;
  1594.                   
  1595.                   resultStringP[resultSize] = ' ';
  1596.                   phoneLabel = GetPhoneLabel(&record, firstPhoneField + 
  1597.                      record.options.phones.displayPhoneForList);
  1598.                   resultStringP[resultSize + 1] = vars->phoneLabelLetters[phoneLabel];
  1599.                   resultSize += 2;
  1600.                   
  1601.                   nextChunkSize = 0;
  1602.                   separatorLength = 2;
  1603.                   }
  1604.                }
  1605.             }
  1606.             
  1607.  
  1608.          
  1609.          // Now copy in the correct field.  lookupNoField can result from
  1610.          // asking for a phone which isn't used.
  1611.          if (field != (AddressFields) addrLookupNoField)
  1612.             {
  1613.             fieldP = record.fields[field];
  1614.             if (fieldP)
  1615.                {
  1616.                nextChunkSize = StrLen(fieldP);
  1617.  
  1618.                if (nextChunkSize > 0)
  1619.                   {
  1620.                   resultStringP = LookupResizeResultString (resultStringH, resultSize + nextChunkSize);
  1621.                   if (! resultStringP) goto exit;
  1622.                   MemMove(resultStringP + resultSize, fieldP, nextChunkSize);
  1623.                   
  1624.                   resultSize += nextChunkSize;
  1625.                   nextChunkSize = 0;
  1626.                   }
  1627.                }
  1628.             }
  1629.          
  1630.          }
  1631.  
  1632.       }
  1633.    
  1634.    // Now null terminate the result string
  1635.    resultStringP = LookupResizeResultString (resultStringH, resultSize + 1);
  1636.    if (! resultStringP) goto exit;
  1637.  
  1638.    resultStringP[resultSize] = '\0';
  1639.  
  1640.    vars->params->resultStringH = resultStringH;
  1641.    MemHandleUnlock(recordH);
  1642.    MemHandleUnlock(resultStringH);
  1643.    
  1644.    return resultStringH;   
  1645.  
  1646.    
  1647. exit:
  1648.    // Error return
  1649.    MemHandleUnlock(recordH);
  1650.    MemHandleFree (resultStringH);
  1651.    vars->params->resultStringH = 0;
  1652.    return (0);
  1653. }
  1654.  
  1655. /***********************************************************************
  1656.  *
  1657.  * FUNCTION:    Lookup
  1658.  *
  1659.  * DESCRIPTION: Present a list of records for the user to select and return
  1660.  * a string formatted to include information from the selected record.
  1661.  *
  1662.  * PARAMETERS:    params - address lookup launch command parameters
  1663.  *
  1664.  * RETURNED:    nothing
  1665.  *                The params will contain a string for the matching record.
  1666.  *
  1667.  * REVISION HISTORY:
  1668.  *         Name   Date      Description
  1669.  *         ----   ----      -----------
  1670.  *         Roger   7/1/96   Initial Revision
  1671.  *
  1672.  ***********************************************************************/
  1673. extern void Lookup (AddrLookupParamsType * params)
  1674. {
  1675.    Err err;
  1676.    DmOpenRef dbP;
  1677.    AddrAppInfoPtr appInfoPtr;
  1678.    UInt      cardNo=0;
  1679.    LocalID   dbID;
  1680.    DmSearchStateType   searchState;
  1681.    FormPtr frm;
  1682.    FormPtr originalForm;
  1683.    LookupVariablesType vars;
  1684.    Boolean uniqueMatch;
  1685.    Boolean completeMatch;
  1686.    UInt mode;
  1687.  
  1688.  
  1689.    // Check the parameters
  1690.    ErrFatalDisplayIf(params->field1 > addrLookupListPhone &&
  1691.       params->field1 != addrLookupNoField, "Bad Lookup request - field1");
  1692.    ErrFatalDisplayIf(params->field2 > addrLookupListPhone &&
  1693.       params->field2 != addrLookupNoField, "Bad Lookup request - field2");
  1694.    
  1695.    
  1696.    // Find the application's data file.
  1697.    err = DmGetNextDatabaseByTypeCreator (true, &searchState, addrDBType,
  1698.                sysFileCAddress, true, &cardNo, &dbID);
  1699.    if (err)
  1700.       {
  1701.       params->resultStringH = 0;
  1702.       return;
  1703.       }
  1704.    
  1705.    // Obey the secret records setting.  Also, we only need to 
  1706.    // read from the database.
  1707.    if (PrefGetPreference(prefHidePrivateRecords))
  1708.       mode = dmModeReadOnly;
  1709.    else
  1710.       mode = dmModeReadOnly | dmModeShowSecret;
  1711.    
  1712.    // Open the address database.
  1713.    dbP = DmOpenDatabase(cardNo, dbID, mode);
  1714.    if (! dbP) 
  1715.       {
  1716.       params->resultStringH = 0;
  1717.       params->uniqueID = 0;
  1718.       return;
  1719.       }
  1720.    
  1721.    // Initialize some of the lookup variables (those needed)
  1722.    vars.params = params;
  1723.    vars.dbP = dbP;
  1724.  
  1725.  
  1726.    // Find how the database is sorted.
  1727.    appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(dbP);
  1728.    vars.sortByCompany = appInfoPtr->misc.sortByCompany;
  1729.    InitPhoneLabelLetters(appInfoPtr, vars.phoneLabelLetters);
  1730.    MemPtrUnlock(appInfoPtr);
  1731.    
  1732.    // Set the mappings from AddressLookupFields to AddressFields.  It is
  1733.    // necessary to initialize the mappings at runtime because static
  1734.    // global variables are not available to this launch code routine.
  1735.    vars.lookupFieldMap[addrLookupName] = name;
  1736.    vars.lookupFieldMap[addrLookupFirstName] = firstName;
  1737.    vars.lookupFieldMap[addrLookupCompany] = company;
  1738.    vars.lookupFieldMap[addrLookupAddress] = address;
  1739.    vars.lookupFieldMap[addrLookupCity] = city;
  1740.    vars.lookupFieldMap[addrLookupState] = state;
  1741.    vars.lookupFieldMap[addrLookupZipCode] = zipCode;
  1742.    vars.lookupFieldMap[addrLookupCountry] = country;
  1743.    vars.lookupFieldMap[addrLookupTitle] = title;
  1744.    vars.lookupFieldMap[addrLookupCustom1] = custom1;
  1745.    vars.lookupFieldMap[addrLookupCustom2] = custom2;
  1746.    vars.lookupFieldMap[addrLookupCustom3] = custom3;
  1747.    vars.lookupFieldMap[addrLookupCustom4] = custom4;
  1748.    vars.lookupFieldMap[addrLookupNote] = note;
  1749.    
  1750.    // Check to see if the lookup string is sufficient for a unique match.
  1751.    // If so we skip presenting the user a lookup dialog and just use the match.
  1752.    AddrLookupLookupString(vars.dbP, params->lookupString, vars.sortByCompany, 
  1753.       vars.params->field1, vars.params->field2, &vars.currentRecord, &vars.currentPhone, 
  1754.       vars.lookupFieldMap, &completeMatch, &uniqueMatch);
  1755.    if (completeMatch && uniqueMatch)
  1756.       {
  1757.       LookupCreateResultString(&vars, vars.currentRecord, vars.currentPhone + firstPhoneField);
  1758.       goto Exit;
  1759.       }
  1760.    
  1761.    // If the user isn't allowed to select a record then return without
  1762.    // a match.
  1763.    if (!params->userShouldInteract)
  1764.       {
  1765.       params->resultStringH = 0;
  1766.       params->uniqueID = 0;
  1767.       goto Exit;
  1768.       }   
  1769.    
  1770.    
  1771.    // Initialize more of the lookup variables
  1772.    vars.currentRecord = noRecord;
  1773.    vars.currentPhone = 0;
  1774.    vars.topVisibleRecord = 0;
  1775.    vars.topVisibleRecordPhone = 0;
  1776.    vars.hideSecretRecords = PrefGetPreference(prefHidePrivateRecords);
  1777.  
  1778.  
  1779.    // Remember the original form   
  1780.    originalForm =  FrmGetActiveForm();
  1781.    
  1782.    // Initialize the dialog.
  1783.    frm = FrmInitForm (LookupView);
  1784.    vars.frm = frm;
  1785.    
  1786.    // Set the title
  1787.    if (params->title)
  1788.       FrmSetTitle(frm, params->title);
  1789.    
  1790.    // Set the paste button
  1791.    if (params->pasteButtonText)
  1792.       CtlSetLabel (FrmGetObjectPtr (frm, FrmGetObjectIndex (frm, LookupPasteButton)), 
  1793.          params->pasteButtonText);
  1794.    
  1795.    FrmSetActiveForm (frm);
  1796.  
  1797.  
  1798.    LookupViewInit (&vars);
  1799.    
  1800.    // Enter the lookup string
  1801.    if (params->lookupString && *params->lookupString != '\0')
  1802.       {
  1803.       FldInsert (FrmGetObjectPtr (frm, FrmGetObjectIndex (frm, LookupLookupField)), 
  1804.          params->lookupString, StrLen(params->lookupString));
  1805.       LookupViewLookupString(&vars, NULL);
  1806.       }
  1807.  
  1808.  
  1809.    FrmDrawForm (frm);
  1810.  
  1811.    FrmSetFocus (frm, FrmGetObjectIndex (frm, LookupLookupField));
  1812.  
  1813.    // Handle events until the user picks a record or cancels
  1814.    if (LookupViewHandleEvent (&vars))
  1815.       {
  1816.       LookupCreateResultString(&vars, vars.currentRecord, vars.currentPhone);
  1817.       }
  1818.    else
  1819.       {
  1820.       params->resultStringH = 0;
  1821.       params->uniqueID = 0;
  1822.       }
  1823.    
  1824.    
  1825.    FrmSetFocus (frm, noFocus);
  1826.    FrmEraseForm (frm);
  1827.    FrmDeleteForm (frm);
  1828.    FrmSetActiveForm (originalForm);
  1829.  
  1830.    
  1831. Exit:
  1832.    DmCloseDatabase (dbP);   
  1833. }
  1834.  
  1835.  
  1836.